'''oto/game.py

This module contains the Game Class.
'''

__authors__ = ['Ryan Rousseau <ryan.rousseau@gmail.com>']
__license__ = '''Oranges to Oranges
Copyright (C) 2006  Ryan Rousseau

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
'''

import sys
import os
import time
import sha

import Pyro.core
import Pyro.naming
import Pyro.EventService.Clients
from Pyro.errors import PyroError, NamingError

from card import *
from constants import *
from deck import *
from publisher import *

__all__ = ['Game']

class Game(Pyro.core.ObjBase):
    def __init__(self, name):
        Pyro.core.ObjBase.__init__(self)
        self.name = name
        self.publisher = None
        self.rules = None
        self.players = {}
        self.submitted_cards = {}
        self.game_status = GAME_STOPPED
        self.judge = None
        self.round_number = 0
        self.round_status = ''
        self.deck = None
        self.trophy_cards = []
        self.num_players = 0

    def get_game_status(self):
        return self.game_status

    def get_num_players(self):
        return len(self.players)

    def get_player(self, name):
        for player in self.players:
            if name == player.name:
                return player
        return None

    def get_trophy_cards(self):
        return self.trophy_cards

    def start(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if not self.deck:
            return ERROR, E_NO_DECK
        if not self.rules:
            return ERROR, E_NO_RULES

        self.publisher = Publisher()
        self.game_status = GAME_IN_PROGRESS
        
        [player.clear_hand() for player in self.players]
        self.submitted_cards = {}
        self.judge = self.players[self.round_number % len(self.players)]

        self.deck.shuffle()
        while len(self.trophy_cards) < self.rules.trophy_cards_per_round:
            self.trophy_cards.append(self.rules.draw_trophy_card(self.deck))

        self.round_status = ROUND_SUBMITTING
        self.publisher.publish_event(GAME_STARTED, '%s has started the game' % name)
        results = self.deal_cards(creds)
        if ERROR == results[0]:
            return results

        return SUCCESS, GAME_STARTED

    def stop(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if name not in [p.name for p in self.players]:
            return ERROR, E_NOT_CONNECTED
        self.game_status = GAME_STOPPED
        self.publisher.publish_event(GAME_STOPPED, '%s has stopped the game' % name)
        return SUCCESS, GAME_STOPPED

    def connect(self, creds):
        name, key = creds
        if GAME_IN_PROGRESS == self.game_status:
            return ERROR, E_GAME_IN_PROGRESS
        if name in [p.name for p in self.players]:
            return ERROR, E_NAME_IN_USE % name

        self.players.append(name, sha.new(key).hexdigest())
        self.publish_event(event.CHAT_ANNOUNCEMENT, '** %s has joined the game **' % name)
        self.publish_event(event.PLAYER_CONNECTED, '%s has joined the game' % name)
        return SUCCESS, S_CONNECT_TO_THE_GAME

    def disconnect(self, creds):
        name, key = creds
        if name not in [p.name for p in self.players]:
            return ERROR, E_NOT_CONNECTED
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS

        self.players.remove(self.get_player(name))

        if name in self.submitted_cards:
            del self.submitted_cards[name]

        self.publisher.publish_event(event.CHAT_ANNOUNCEMENT, '** %s has left the game **' % name)
        self.publisher.publish_event(event.PLAYER_DISCONNECTED, '%s has left the game' % name)
        return SUCCESS, S_DISCONNECTED

    def get_players(self):
        return self.players.keys()

    def get_trophies(self, player):
        return self.get_player(player).trophies

    def set_num_players(self, creds, num_players):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if GAME_IN_PROGRESS == self.game_status:
            return ERROR, E_GAME_IN_PROGRESS
        if name not in [p.name for p in self.players]:
            return ERROR, E_NOT_CONNECTED
        self.num_players = num_players
        return SUCCESS, S_NUM_PLAYERS_SET

    def set_deck(self, creds, deck_name):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if GAME_IN_PROGRESS == self.game_status:
            return ERROR, E_GAME_IN_PROGRESS
        self.deck = Deck(deck_name)
        return SUCCESS, S_DECK_SET

    def deal_cards(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.players[name]:
            return ERROR, E_INVALID_CREDENTIALS
        if GAME_IN_PROGRESS != self.game_status:
            return ERROR, E_GAME_NOT_STARTED

        for player in self.players:
            while player.needs_cards():
                self.player.deal_card(self.deck.draw_noun())

        self.publisher.publish_event(CARDS_DEALT, 'Cards have been dealt')
        return SUCCESS, S_CARDS_DEALT

    def get_hand(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if GAME_IN_PROGRESS != self.game_status:
            return ERROR, E_GAME_IN_PROGRESS
        return self.get_player(name).hand

    def submit_card(self, creds, card):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if name == self.judge:
            return ERROR, E_NOT_JUDGE
        if name in self.submitted_cards:
            return ERROR, E_ALREADY_PLAYED
        if ROUND_SUBMITTING != self.round_status:
            return ERROR, E_JUDGING
        if card not in self.get_player(name).hand:
            return ERROR, E_NOT_YOUR_CARD
        
        self.submitted_cards[name] = card
        self.get_player(name).hand.remove(card)
        self.publisher.publish_event(CARD_SUBMITTED, 'A card was submitted')
        
        if len(self.submitted_cards) == len(self.players) - 1:
            self.round_status = ROUND_JUDGING
            self.publisher.publish_event(SUBMITTING_STOPPED, 'The submitting phase is over')
            self.publisher.publish_event(JUDGING_STARTED, 'The judging phase has started')

        return SUCCESS, S_CARD_SUBMITTED

    def is_judge(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        return name == self.judge

    def judge_card(self, creds, card):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if name != self.judge:
            return ERROR, E_NOT_JUDGE
        if card not in self.submitted_cards.values():
            return ERROR, E_INVALID_CARD % card

        for player, p_card in self.submitted_cards.items():
            if p_card == card:
                break

        round_title = self.round_card.title
        title = card.title
        self.deck.add_noun(p_card)
        del self.submitted_cards[player]

        if {} != self.submitted_cards:

            self.publisher.publish_event(CHAT_ANNOUNCEMENT, '** "%s" was rejected **' % title)
            self.publisher.publish_event(CARD_REJECTED, 'A card was rejected')
        else:
            self.get_player(player).add_trophy(self.round_card)
            self.round_number += 1
            self.judge = self.players[self.round_number % len(self.players)]
            self.round_card = self.deck.draw_adj()
            self.round_status = ROUND_SUBMITTING
            self.publish_event(CHAT_ANNOUNCEMENT, '** %s won "%s" with "%s" **' % (player, round_title, title))
            self.publish_event(JUDGING_OVER, 'The judging phase is over')
            self.deal_cards(creds)

    ### may delete check_round_status... ###

    def check_round_status(self, creds):
        '''check_round_status(creds)

'''
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS
        if ROUND_SUBMITTING == self.round_status:
            if name in self.submitted_cards:
                return ROUND_SUBMITTING, False
            return 'submit', self.judge != name
        elif status.ROUND_JUDGING == self.round_status:
            return 'judge', self.judge == name
        else:
            return 'pass', False

    def get_submitted_cards(self, creds):
        name, key = creds
        if sha.new(key).hexdigest() != self.players[name]:
            return 'Error', 'Invalid credentials'
        if name != self.judge:
            return 'Error', 'You are not the judge for this round'
        elif status.ROUND_SUBMITTING == self.round_status:
            return 'Error', 'Cards are still being submitted this round'
        return 'Success', self.submitted_cards.values()

    def chat(self, creds, message):
        name, key = creds
        if sha.new(key).hexdigest() != self.get_player(name).key:
            return ERROR, E_INVALID_CREDENTIALS

        if message.startswith('/me '):
            message = '* %s %s' % (name, message.split(' ', 1)[1])
        else:
            message = '%s said, "%s"' % (name, message)
        self.publisher.publish_event(event.CHAT_TALK, message)
        return SUCCESS, S_CHAT
